package com.xiaoxx.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.xiaoxx.coin.enums.CurrencyTypeEnum;
import com.xiaoxx.coin.enums.OrderTypesEnum;
import com.xiaoxx.coin.enums.TimeInForceEnum;
import com.xiaoxx.coin.impl.UMFuturesClientImpl;
import com.xiaoxx.coin.impl.futures.Account;
import com.xiaoxx.coin.po.res.*;
import com.xiaoxx.config.ThreadUtils;
import com.xiaoxx.model.dto.coin.CommonOrderDto;
import com.xiaoxx.model.entity.coin.BinanceApikey;
import com.xiaoxx.model.entity.coin.BinanceOrder;
import com.xiaoxx.model.vo.coin.AccountInfoVo;
import com.xiaoxx.service.BinanceApikeyService;
import com.xiaoxx.service.BinanceOderService;
import com.xiaoxx.service.BinanceTradingService;
import com.xiaoxx.utils.BeanDtoVoUtil;
import com.xiaoxx.utils.date.LocalDateTimeUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class BinanceTradingServiceImpl implements BinanceTradingService {
    @Autowired
    private BinanceApikeyService apikeyService;
    @Autowired
    private BinanceOderService binanceOderService;

    @Override
    public AccountInfoVo getAccountInfo(String apiKey, String secretKey) {
        LinkedHashMap<String, Object> parameters = new LinkedHashMap<>();
        Account account = new UMFuturesClientImpl(apiKey, secretKey).account();
        List<AccountBalanceResponse> accountBalanceResponse = account.futuresAccountBalance(parameters);

        if (!accountBalanceResponse.isEmpty()) {
            List<AccountBalanceResponse> usdtAccounts = accountBalanceResponse.stream().filter(i -> i.getAsset().equals(CurrencyTypeEnum.USDT.getCode())).collect(Collectors.toList());
            return BeanDtoVoUtil.convert(usdtAccounts.get(0), AccountInfoVo.class);
        }
        return new AccountInfoVo();
    }

    @Override
    public AccountBillingInfoResponse getAccountTrading(String apiKey, String secretKey) {
        LinkedHashMap<String, Object> parameters = new LinkedHashMap<>();
        Account account = new UMFuturesClientImpl(apiKey, secretKey).account();
        AccountBillingInfoResponse billingInfoResponse = account.accountInformation(parameters);
        return billingInfoResponse;
    }

    @Override
    public List<CurrentAllOpenOrdersResponse> currentAllOpenOrders(String apiKey, String secretKey) {
        LinkedHashMap<String, Object> parameters = new LinkedHashMap<>();
        Account account = new UMFuturesClientImpl(apiKey, secretKey).account();
        return account.currentAllOpenOrders(parameters);
    }

    @Override
    public BinanceApikey getBinanceApikey(String userId) {
        BinanceApikey binanceApikey = apikeyService.lambdaQuery().eq(BinanceApikey::getUserId, userId).last("limit 1").one();
        return binanceApikey;
    }

    @Override
    public CancelOrderResponse cancelOrder(String apiKey, String secretKey, String symbol, Long orderId) {
        LinkedHashMap<String, Object> parameters = new LinkedHashMap<>();
        parameters.put("symbol", symbol);
        parameters.put("orderId", orderId);
        Account account = new UMFuturesClientImpl(apiKey, secretKey).account();
        CancelOrderResponse cancelOrderResponse = account.cancelOrder(parameters);
        return cancelOrderResponse;
    }

    @Override
    public List<CancelOrderResponse> cancelBatchOrders(String apiKey, String secretKey, String symbol, List<Long> orderIdList) {
        LinkedHashMap<String, Object> parameters = new LinkedHashMap<>();
        parameters.put("symbol", symbol);
        parameters.put("orderIdList", JSONObject.toJSONString(orderIdList));
        Account account = new UMFuturesClientImpl(apiKey, secretKey).account();
        List<CancelOrderResponse> cancelOrderResponse = account.cancelBatchOrders(parameters);
        return cancelOrderResponse;
    }

    @Override
    public List<UserTradesResponse> queryUserTrades(String apiKey, String secretKey, String symbol, Long orderId, LocalDateTime startTime, LocalDateTime endTime) {
        LinkedHashMap<String, Object> parameters = new LinkedHashMap<>();
        if (StringUtils.hasLength(symbol)) {
            parameters.put("symbol", symbol);
        }
        if (orderId != null) {
            parameters.put("orderId", orderId);
        }
        if (startTime != null && endTime != null) {
            parameters.put("startTime", LocalDateTimeUtil.parseMillisecond(startTime));
            parameters.put("endTime", LocalDateTimeUtil.parseMillisecond(endTime));
        }
        Account account = new UMFuturesClientImpl(apiKey, secretKey).account();
        return account.accountTradeList(parameters);
    }

    @Override
    public CommonOrderResponse createOrder(String apiKey, String secretKey, CommonOrderDto dto) {
        LinkedHashMap<String, Object> parameters = new LinkedHashMap<>();
        OrderTypesEnum type = dto.getType();
        switch (type) {
            case LIMIT:
                limitParameters(parameters, dto);
                break;
            case MARKET:
                break;
            case STOP:
                stopParameters(parameters, dto);
                break;
            case STOP_MARKET:
                stopMarketParameters(parameters, dto);
                break;
            case TAKE_PROFIT:
                takeProfitParameters(parameters, dto);
                break;
            case TAKE_PROFIT_MARKET:
                takeProfitMarketParameters(parameters, dto);
                break;
            case TRAILING_STOP_MARKET:
                break;
            default:
                throw new RuntimeException("未知订单类型");
        }
        Account account = new UMFuturesClientImpl(apiKey, secretKey).account();
        CommonOrderResponse commonOrderResponse = account.newOrder(parameters);
        ThreadUtils.asyncSumbit(() -> {
            createSystemOrderInfo(dto.getUserId(), commonOrderResponse);
        });
        return commonOrderResponse;
    }


    @Override
    public CommonOrderResponse queryOrder(String apiKey, String secretKey, String symbol, Long orderId) {
        LinkedHashMap<String, Object> parameters = new LinkedHashMap<>();
        parameters.put("symbol", symbol);
        parameters.put("orderId", orderId);
        Account account = new UMFuturesClientImpl(apiKey, secretKey).account();
        CommonOrderResponse commonOrderResponse = account.queryOrder(parameters);
        return commonOrderResponse;
    }


    private void stopMarketParameters(LinkedHashMap<String, Object> parameters, CommonOrderDto dto) {
        parameters.put("type", OrderTypesEnum.STOP_MARKET.getType());
        parameters.put("symbol", dto.getSymbol());
        parameters.put("side", dto.getSide().getSide());
        parameters.put("positionSide", dto.getPositionSide().getCode());
        parameters.put("closePosition", true);
        parameters.put("stopPrice", dto.getStopPrice());
    }

    private void stopParameters(LinkedHashMap<String, Object> parameters, CommonOrderDto dto) {
        parameters.put("type", OrderTypesEnum.STOP.getType());
        parameters.put("symbol", dto.getSymbol());
        parameters.put("price", dto.getPrice());
        parameters.put("side", dto.getSide().getSide());
        parameters.put("quantity", dto.getQuantity());
        parameters.put("positionSide", dto.getPositionSide().getCode());
        parameters.put("timeInForce", TimeInForceEnum.GTC.getCode());
        BigDecimal price = dto.getPrice();
//        price.add(price.multiply(new BigDecimal("1.25").divide(new BigDecimal("100"))))
//        parameters.put("stopPrice", new BigDecimal("8.13"));
    }

    private void takeProfitMarketParameters(LinkedHashMap<String, Object> parameters, CommonOrderDto dto) {
        parameters.put("type", OrderTypesEnum.TAKE_PROFIT_MARKET.getType());
        parameters.put("symbol", dto.getSymbol());
        parameters.put("side", dto.getSide().getSide());
        parameters.put("positionSide", dto.getPositionSide().getCode());
        parameters.put("closePosition", true);
        parameters.put("stopPrice", dto.getStopPrice());
    }

    private void takeProfitParameters(LinkedHashMap<String, Object> parameters, CommonOrderDto dto) {
        parameters.put("type", OrderTypesEnum.TAKE_PROFIT.getType());
        parameters.put("symbol", dto.getSymbol());
        parameters.put("side", dto.getSide().getSide());
        parameters.put("positionSide", dto.getPositionSide().getCode());
        parameters.put("closePosition", true);
        parameters.put("stopPrice", dto.getStopPrice());
    }

    /**
     * TODO 参数校验
     *
     * @param parameters
     * @param dto
     */
    private void limitParameters(LinkedHashMap<String, Object> parameters, CommonOrderDto dto) {
        parameters.put("type", OrderTypesEnum.LIMIT.getType());
        parameters.put("symbol", dto.getSymbol());
        parameters.put("price", dto.getPrice());
        parameters.put("side", dto.getSide().getSide());
        parameters.put("quantity", dto.getQuantity());
        parameters.put("positionSide", dto.getPositionSide().getCode());
        parameters.put("timeInForce", TimeInForceEnum.GTC.getCode());
    }

    private void createSystemOrderInfo(String userId, CommonOrderResponse commonOrderResponse) {
        BinanceOrder binanceOrder = new BinanceOrder();
        binanceOrder.setUserId(userId);
        binanceOrder.setBinanceOrderId(commonOrderResponse.getOrderId());
        binanceOrder.setSymbol(commonOrderResponse.getSymbol());
        binanceOrder.setCumQuote(commonOrderResponse.getCumQuote());
        binanceOrder.setExecutedQty(commonOrderResponse.getExecutedQty());
        binanceOrder.setOrigQty(commonOrderResponse.getOrigQty());
        binanceOrder.setPrice(commonOrderResponse.getPrice());
        binanceOrder.setSide(commonOrderResponse.getSide());
        binanceOrder.setPositionSide(commonOrderResponse.getPositionSide());
        binanceOrder.setStatus(commonOrderResponse.getStatus());
        binanceOrder.setType(commonOrderResponse.getType());
        binanceOrder.setTimeinForce(commonOrderResponse.getTimeInForce());
        binanceOrder.setDepartureTime(LocalDateTimeUtil.parseTimestamp(commonOrderResponse.getUpdateTime()));
        binanceOderService.save(binanceOrder);
    }
}
